package com.guigusuqi.payment.controller;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guigusuqi.app.entity.LoginWxUser;
import com.guigusuqi.app.entity.Registration;
import com.guigusuqi.app.entity.WxUser;
import com.guigusuqi.app.service.RegistrationService;
import com.guigusuqi.commonutils.common.KsdStaticParameter;
import com.guigusuqi.commonutils.utils.*;
import com.guigusuqi.commonutils.vo.Result;
import com.guigusuqi.payment.entity.RegistrationOrder;
import com.guigusuqi.payment.entity.UserOrderRegistration;
import com.guigusuqi.payment.service.RegistrationOrderService;
import com.guigusuqi.payment.service.UserOrderRegistrationService;
import io.netty.util.internal.ResourcesUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author suqi
 * @since 2022-04-22
 */
@Log4j2
@RestController
@Api(tags = {"wx-Native支付"})
@RequestMapping("/")
public class NativeController
{
    //挂号
    @Resource
    private RegistrationService registrationService;

    @Resource
    private RegistrationOrderService registrationOrderService;

    @Resource
    private UserOrderRegistrationService userOrderRegistrationService;

    @Resource
    private RedisCache redisCache;

    @ApiOperation("付款订单Api,根据传入的订单号 生成付款二维码")
    @GetMapping("/wxpay/{registrationId}")
    public Result wxPay(@PathVariable("registrationId") String registrationId) throws Exception
    {
        Registration registration = registrationService.getById(registrationId);
        if(Objects.isNull(registration))
        {
            return Result.fail().message("挂号订单为空");
        }

        //封装请求参数
        Map<String,Object> map = new HashMap<>();
        map.put("mchid", KsdStaticParameter.mchId);
        map.put("appid",KsdStaticParameter.appId);

        //流水号，产生唯一的id，与业务流水挂钩
        map.put("out_trade_no",new SnowflakeIdWorker(1,1).nextId() + "");

        map.put("description","Image形象店-深圳腾大-QQ公仔");

        //异步接收微信支付结果通知的回调地址callback
        map.put("notify_url",KsdStaticParameter.notifyUrl);

        //订单金额 单位分
        Map<String,Object> amount = new HashMap<>();

        //订单金额，这个得前端自己传
        amount.put("total",Integer.parseInt(getMoney(registration.getPrice())));
        amount.put("currency","CNY");
        map.put("amount",amount);


        String token = GetTokenByAuthorizationUtils.getToken();
        LoginWxUser loginWxUser = redisCache.getCacheObject("TOKEN_"+token);
        WxUser user = loginWxUser.getUser();

        //附属参数，方便微信回调把数据存入数据库
        Map<String,Object> attachMap = new HashMap<>();
        attachMap.put("openid",user.getOpenid());
        attachMap.put("nickname",user.getNickname());
        attachMap.put("registrationId",registrationId);
        attachMap.put("price","0.1");
        //将请求头转换为string
        map.put("attach", JsonUtil.obj2String(attachMap));

        //将map转换为json字符串，开始微信请求
        ObjectMapper objectMapper = new ObjectMapper();
        String body = objectMapper.writeValueAsString(map);

        //请求统一微信native下单接口
        Map<String, Object> stringObjectMap = HttpUtils.doPost(KsdStaticParameter.unifiedOrderUrl, body);

        //获取微信返回的code_Url
        String codeUrl = stringObjectMap.get("code_url").toString();

        try {
            //生成二维码配置
            ByteArrayOutputStream output = new ByteArrayOutputStream();

            //获取logo
            String logoPath = ResourceUtils.getFile(KsdStaticParameter.logo).getAbsolutePath();

            //生成二维码
            BufferedImage bufferedImage = QRCodeUtil.encode(codeUrl, logoPath, false);

            //通过JPEG格式传递给前端
            ImageOutputStream imageOutput = ImageIO.createImageOutputStream(output);
            //将二维码以JPEG的格式输入到流中
            ImageIO.write(bufferedImage,"JPEG",imageOutput);
            imageOutput.close();

            ByteArrayInputStream inputStream = new ByteArrayInputStream(output.toByteArray());

            byte[] bytes = FileCopyUtils.copyToByteArray(inputStream);
            return Result.success().data("rows",bytes);
        } catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 支付回调
     * @param body
     * @param request
     * @return
     */
    @ApiOperation("支付回调")
    @PostMapping("/api/pay/callback")
    public Result orderPayCallback(@RequestBody Map body, HttpServletRequest request)
    {
        log.info("微信支付回调开始=======================>");
        //1.获取微信支付回调的签名信息
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        //获取随机数
        String nonce = request.getHeader("Wechatpay-Nonce");

        ObjectMapper objectMapper = new ObjectMapper();
        //解析报文
        try {
            String data = objectMapper.writeValueAsString(body);

            String message = timestamp + "\n" + nonce + "\n" + data + "\n";

            //获取应答签名
            String sign = request.getHeader("Wechatpay-Signature");

            //获取平台对应证书key
            String serialNo = request.getHeader("Wechatpay-Serial");

            //如果yaml的证书公钥不包含微信传递过来的证书公钥，则获取平台证书
            if (!KsdStaticParameter.certificateMap.containsKey(serialNo))
            {
                KsdStaticParameter.certificateMap = WechatPayUtils.refreshCertificate();
            }
            //获取证书value
            X509Certificate x509Certificate = KsdStaticParameter.certificateMap.get(serialNo);
            //核实签名是否正确
            if(!WechatPayUtils.verify(x509Certificate,message.getBytes(),sign))
            {
                throw new IllegalArgumentException("微信支付签名验证失败:" + message);
            }
            log.info("签名验证成功");
            //获取微信传过来的请求体的数据
            Map<String,String> resource = (Map) body.get("resource");
            //回调报文解析
            AesUtil aesUtil = new AesUtil(KsdStaticParameter.v3Key.getBytes());
            //解密json字符串
            //使用key、nonce和associated_data，对数据密文resource.ciphertext进行解密，得到JSON形式的资源对象
            String decryptToString = aesUtil.decryptToString(
                    resource.get("associated_data").getBytes(),
                    resource.get("nonce").getBytes(),
                    resource.get("ciphertext"));
            log.info("2------------->decryptToString====>{}", decryptToString);
            //获取微信支付返回的信息
            Map<String, Object> jsonData = objectMapper.readValue(decryptToString, Map.class);
            //支付状态的判断 如果是success就代表支付成功
            if("SUCCESS".equals(jsonData.get("trade_state")))
            {
                //获取支付的交易单号，流水号，和附属参数
                String out_trade_no = jsonData.get("out_trade_no").toString();
                String transaction_id = jsonData.get("transaction_id").toString();

                //附属参数
                String attach = jsonData.get("attach").toString();

                //根据订单号查询支付状态，如果未支付，更新支付状态 为已支付
                log.info("3----------->微信支付成功,支付流水号是：{},附属参数是：{}", out_trade_no, attach);
                log.info("4----------->微信支付成功,支付流水号是：{},{}", transaction_id);

                //把附属参数转换为map
                HashMap<String,Object> hashMap = JsonUtil.string2Obj(attach, HashMap.class);

                //保存用户订单明细
                RegistrationOrder registrationOrder = new RegistrationOrder();

                registrationOrder.setOpenid(String.valueOf(hashMap.get("openid")));
                registrationOrder.setNickname(String.valueOf(hashMap.get("nickname")));
                registrationOrder.setTradeno(out_trade_no);
                registrationOrder.setPrice(String.valueOf(hashMap.get("price")));

                System.out.println(registrationOrder.toString());

                boolean saveRegistrationOrder = registrationOrderService.save(registrationOrder);

                if(saveRegistrationOrder)
                {
                    String orderId = registrationOrder.getId();
                    String registrationId = String.valueOf(hashMap.get("registrationId"));
                    boolean saveUserOrderRegistration = userOrderRegistrationService.save(new UserOrderRegistration(orderId, registrationId));
                    if(saveUserOrderRegistration)
                    {
                        return Result.success().message("支付成功");
                    }
                }
            }
        }catch (Exception e)
        {
            return Result.fail().message("支付失败");
        }
        return Result.fail().message("支付失败");
    }

    /**
     * 元转换成分
     *
     * @param money
     * @return
     */
    private static String getMoney(String money)
    {
        if (money == null || money.equalsIgnoreCase("0"))
        {
            return "";
        }
        /*
         * 例如 0.12元        12元          1.2元
         *   index : 1       index :-1     index:1
         *   length : 4      length:2      length:3
         *   amLong : 12分   amLong:1200分  amLong:120
         * */
        // 金额转化为分为单位
        // 处理包含, ￥ 或者$的金额
        String currency = money.replaceAll("\\$|\\￥|\\,", "");
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong = 0l;
        //没有小数
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
            // 传过来的数至少有两位小数
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
            //传过来的数有一位小数
        } else if (length - index == 2)
        {
            String substring = currency.substring(0, index + 2);
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
            //小数后面没数
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }
}

